//
// Created by libtity on 7/2/24.
//

#include "compiler.h"


void Compiler::compile(const char* path)
{
    initTable();

    char ch;

    fp1 = fopen(path, "r");   // 创建文件指针及打开文本文件
    fp2.open(R"(/home/libtity/complier/EXP2/output.txt)", ios::out);
    if ( fp1 == nullptr ) {
        printf("文件 %s 打开时发生错误", path);
        return;
    }

    ch = fgetc(fp1);
    while ( ch != EOF ) {
        input[0] = ch;
        index    = 1;

        // 前置工作，删去注释，删去空白，删去换行
        // 获取字符串类型，获取以空格为分界的单词
        // 得到当前行数，用于报错时的输出
        int type = table[(int)ch];
        switch ( type ) {
        // 换行符
        case HUANHANG:
            current_line++;
            break;
        // 注释
        case COMMENT:
            deleteComment();
            break;
        // 字符串
        case STRING:
            getString();
            break;

        case WHITE:
            break;

        case JIEFU:
            break;

        default:
            getToSpace();
            break;
        }
        input[index] = '\0';
        //        if ( type != WHITE && type != COMMENT && type != HUANHANG ) {
        //            cout << "current_line : " << current_line << " : " << input << endl;
        //        }

        scanner();

        if ( num == INF && word.empty() ) {
            if ( ch != ' ' && ch != '\n' && ch != '\r' && type != WHITE && type != HUANHANG ) {
                cerr << "wrong: input error" << endl;
                cerr << "current_line : " << current_line << endl;
            }
        }

        if ( num != INF ) {
            //            cout << token << " ";
            //            cout << num << endl;
            fp2 << "( " << token << ", " << num << " )" << endl;
            num = INF;
        }
        if ( !word.empty() ) {
            //            cout << token << " ";
            //            cout << word << endl;
            fp2 << "( " << token << ", " << word << " )" << endl;
            word.clear();
        }


        if ( c == '\0' ) {
            ch = fgetc(fp1);
        }
        else {
            ch = c;
            c  = '\0';
        }
    }

    fclose(fp1);
}

void Compiler::scanner()
{
    int  p  = 0;
    char ch = input[p];
    // 获取单词类型
    if ( (ch <= 'z' && ch >= 'a') || (ch <= 'Z' && ch >= 'A') ) {
        token = "IDENFR";

        int sign = 1;
        while ( (ch <= 'z' && ch >= 'a') || (ch <= 'Z' && ch >= 'A') || (ch <= '9' && ch >= '0') ) {
            if ( ch <= '9' && ch >= '0' ) {
                sign = 0;
            }
            ch = input[p++];
        }
        // 检查是否整个字符串都不包含其他字符，不然报错
        if ( ch != '\0' ) {
            cerr << "wrong: word is not right" << endl;
            cerr << "current_line is " << current_line << endl;
            exit(1);
        }

        // 没有报错
        word = input;
        num  = INF;
        // sign == 1,表示全都是字符，有可能是保留字
        if ( sign ) {
            for ( const auto& i : reserved ) {
                if ( word == i ) {
                    token = upper(i) + "TK";
                    break;
                }
            }
        }
    }
    else if ( ch <= '9' && ch >= '0' ) {
        token = "INTCON";
        while ( ch <= '9' && ch >= '0' ) {
            ch = input[p++];
        }
        // 检查是否整个字符串都不包含其他字符，不然报错
        if ( ch != '\0' ) {
            cerr << "wrong: word is not right" << endl;
            cerr << "current_line is " << current_line << endl;
            exit(1);
        }
        num  = stoi(input);
        word = "";
    }
    else if ( ch == '"' ) {
        token = "STRCON";
        word  = input;
        num   = INF;
    }
    else {
        int sign = 0;
        for ( int i = 0; i < jiefu.size(); ++i ) {
            if ( ch == jiefu[i] ) {
                sign = 1;
                break;
            }
        }
        if ( sign ) {
            switch ( ch ) {
            case '+':
                ch = fgetc(fp1);
                if ( ch == '+' ) {
                    token = "AAA";
                    word  = "++";
                }
                else {
                    token = "PLUS";
                    word  = "+";
                    c     = ch;
                }
                break;
            case '-':
                ch = fgetc(fp1);
                if ( ch == '-' ) {
                    token = "MMM";
                    word  = "--";
                }
                else {
                    token = "MINU";
                    word  = "-";
                    c     = ch;
                }
                break;
            case '*':
                token = "MULT";
                word  = "*";
                break;
            case '(':
                token = "LPA";
                word  = "(";
                break;
            case ')':
                token = "RPA";
                word  = ")";
                break;
            case '[':
                token = "LBRA";
                word  = "[";
                break;
            case ']':
                token = "RBRA";
                word  = "]";
                break;
            case '{':
                token = "LBRACE";
                word  = "{";
                break;
            case '}':
                token = "RBRACE";
                word  = "}";
                break;
            case ',':
                token = "COMMA";
                word  = ",";
                break;
            case ';':
                token = "SEMICN";
                word  = ";";
                break;
            case ':':
                token = "COLON";
                word  = ":";
                break;
            case '=':
                token = "ASSIGN";
                word  = "=";
                break;
            case '<':
                ch = fgetc(fp1);
                if ( ch == '=' ) {
                    token = "LEQ";
                    word  = "<=";
                }
                else {
                    token = "LSS";
                    word  = "<";
                    c     = ch;
                }
                break;
            case '>':
                ch = fgetc(fp1);
                if ( ch == '=' ) {
                    token = "GEQ";
                    word  = ">=";
                }
                else {
                    token = "GRE";
                    word  = ">";
                    c     = ch;
                }
                break;
            case '!':
                token = "NOT";
                word  = "!";
                break;
            case '&':
                ch = fgetc(fp1);
                if ( ch == '&' ) {
                    token = "AND";
                    word  = "&&";
                }
                else {
                    c = ch;
                }
                break;
            }
        }
    }
}

void Compiler::deleteComment()
{
    // 当前字符串设置为空
    index = 0;

    char ch = fgetc(fp1);
    if ( ch == '/' ) {
        ch = fgetc(fp1);
        while ( ch != EOF ) {
            if ( ch == 10 ) {
                current_line++;
                break;
            }
            ch = fgetc(fp1);
        }
    }
    else if ( ch == '*' ) {
        ch = fgetc(fp1);
        while ( ch != EOF && ch != '*' ) {
            if ( ch == 10 ) {
                current_line++;
            }
            ch = fgetc(fp1);
        }
        if ( ch == EOF ) {
            cerr << "wrong: there is no end to a comment " << endl;
            cerr << "current_line is " << current_line << endl;
            exit(1);
        }
        else {
            ch = fgetc(fp1);
            if ( ch != '/' ) {
                cerr << "wrong: there is no end to a comment" << endl;
                cerr << "current_line is " << current_line << endl;
                exit(1);
            }
        }
    }
    if ( ch != ' ' ) {
        c            = ch;
        index        = 1;
        input[index] = '\0';
    }
    if ( ch == 10 ) {
        current_line++;
    }
}

void Compiler::getString()
{
    char ch = fgetc(fp1);
    while ( ch != EOF && ch != '"' ) {
        // 去除换行符
        if ( ch != 10 ) {
            input[index] = ch;
            index++;
        }
        else {
            input[index] = '\n';
            index++;
            current_line++;
        }

        ch = fgetc(fp1);
    }
    if ( ch == '"' ) {
        input[index] = ch;
        index++;
    }
    else {
        cerr << "wrong: there is no end to a string" << endl;
        cerr << "current_line is " << current_line << endl;
        exit(1);
    }
}

void Compiler::getToSpace()
{
    char ch = fgetc(fp1);
    while ( (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') ) {
        input[index] = ch;
        index++;
        ch = fgetc(fp1);
    }
    if ( ch != EOF ) {
        c = ch;
    }
}

void Compiler::initTable()
{
    for ( int i = 0; i < 256; ++i ) {
        if ( (i >= 'A' && i <= 'Z') || (i >= 'a' && i <= 'z') ) {
            table[i] = WORD;
        }
        else if ( i >= '0' && i <= '9' ) {
            table[i] = NUMBER;
        }
        else if ( i == '/' ) {
            table[i] = COMMENT;
        }
        else if ( i == 10 ) {
            table[i] = HUANHANG;
        }
        else if ( i == ' ' || i == '\r' ) {
            table[i] = WHITE;
        }
        else if ( i == '"' ) {
            table[i] = STRING;
        }
        else if ( i == ';' ) {
            table[i] = FENHAO;
        }
        else {
            int sign = 1;
            for ( int j = 0; j < jiefu.size(); ++j ) {
                if ( i == jiefu[j] ) {
                    table[i] = JIEFU;
                    sign     = 0;
                    break;
                }
            }
            if ( sign ) {
                table[i] = 0;
            }
        }
    }
    current_line = 0;
    num          = INF;
    word         = "";
    c            = '\0';
}


string Compiler::upper(string s)
{
    for ( int i = 0; i < s.size(); ++i ) {
        s[i] -= 32;
    }
    return s;
}
